package serv_cmd

import (
	"context"
	"io"
	"time"

	"atomgit.com/openlinksaas/proto-gen-go.git/swarm_proxy_api"
	"atomgit.com/openlinksaas/swarm_api_proxy/utils"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/pkg/stdcopy"
	"github.com/google/uuid"
)

type LogItem struct {
	LogId        string
	Cancel       context.CancelFunc
	LogChan      chan []byte
	LastReadTime int64
}

func (apiImpl *SwarmProxyApiImpl) OpenLog(ctx context.Context, req *swarm_proxy_api.OpenLogRequest) (*swarm_proxy_api.OpenLogResponse, error) {
	//检查权限
	tokenInfo, err := apiImpl.getTokenInfo(apiImpl.cfg.LinkSaasAddr, req.Token)
	if err != nil {
		return &swarm_proxy_api.OpenLogResponse{
			Code:   swarm_proxy_api.OpenLogResponse_CODE_WRONG_TOKEN,
			ErrMsg: "错误的访问令牌",
		}, nil
	}
	c, err := utils.GetDockerClient(apiImpl.cfg.ServerUrl, apiImpl.cfg.CertPath)
	if err != nil {
		return nil, err
	}
	defer c.Close()

	exist, err := apiImpl.existProject(ctx, c, tokenInfo.ProjectId)
	if err != nil {
		return nil, err
	}
	if !exist {
		return &swarm_proxy_api.OpenLogResponse{
			Code:   swarm_proxy_api.OpenLogResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	if !tokenInfo.Admin {
		userPerm := apiImpl.getUserPerm(ctx, tokenInfo.ProjectId, tokenInfo.MemberUserId)
		if !userPerm.Logs {
			return &swarm_proxy_api.OpenLogResponse{
				Code:   swarm_proxy_api.OpenLogResponse_CODE_NO_PERMISSION,
				ErrMsg: "没有权限",
			}, nil
		}
	}

	containerItem, err := c.ContainerInspect(ctx, req.ContainerId)
	if err != nil {
		return &swarm_proxy_api.OpenLogResponse{
			Code:   swarm_proxy_api.OpenLogResponse_CODE_NO_CONTAINER,
			ErrMsg: "容器不存在",
		}, nil
	}

	logCtx, cancel := context.WithCancel(context.Background())
	logReader, err := c.ContainerLogs(logCtx, req.ContainerId, types.ContainerLogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Timestamps: true,
		Follow:     true,
		Tail:       "200",
		Details:    false,
	})
	if err != nil {
		cancel()
		return nil, err
	}
	logId := uuid.NewString()
	logChan := make(chan []byte, 100)
	apiImpl.logItemMap.Store(logId, &LogItem{
		LogId:        logId,
		Cancel:       cancel,
		LogChan:      logChan,
		LastReadTime: time.Now().Unix(),
	})

	go apiImpl.readLog(logReader, logChan, containerItem.Config.Tty)
	return &swarm_proxy_api.OpenLogResponse{
		Code:  swarm_proxy_api.OpenLogResponse_CODE_OK,
		LogId: logId,
	}, nil
}

type LogWriter struct {
	logChan chan<- []byte
}

func (lw *LogWriter) Write(p []byte) (n int, err error) {
	buf := make([]byte, len(p))
	copy(buf, p)
	lw.logChan <- buf
	return len(p), nil
}

func (apiImpl *SwarmProxyApiImpl) readLog(logReader io.ReadCloser, logChan chan<- []byte, tty bool) {
	defer func() {
		recover()
	}()

	defer logReader.Close()

	w := &LogWriter{
		logChan: logChan,
	}

	if tty {
		io.Copy(w, logReader)
	} else {
		stdcopy.StdCopy(w, w, logReader)
	}
}

func (apiImpl *SwarmProxyApiImpl) ReadLog(ctx context.Context, req *swarm_proxy_api.ReadLogRequest) (*swarm_proxy_api.ReadLogResponse, error) {
	defer func() {
		recover()
	}()

	logItemValue, ok := apiImpl.logItemMap.Load(req.LogId)
	if !ok {
		return &swarm_proxy_api.ReadLogResponse{
			Code:   swarm_proxy_api.ReadLogResponse_CODE_NO_LOG,
			ErrMsg: "日志句柄不存在",
		}, nil
	}
	logItem := logItemValue.(*LogItem)
	logItem.LastReadTime = time.Now().Unix()
	apiImpl.logItemMap.Store(logItem.LogId, logItem)

	select {
	case data := <-logItem.LogChan:
		return &swarm_proxy_api.ReadLogResponse{
			Code: swarm_proxy_api.ReadLogResponse_CODE_OK,
			Data: string(data),
		}, nil
	case <-time.After(30 * time.Second):
		return &swarm_proxy_api.ReadLogResponse{
			Code: swarm_proxy_api.ReadLogResponse_CODE_OK,
			Data: "",
		}, nil
	}

}
